function PlanetaryPositions(){	
	
	var pp = this;
	var undefined = "undefined";
	
	if(!window.$NumberUtils) new NumberUtils();
	pp.epoch = new Date(1999, 11, 31)
	pp.NumberUtils = $NumberUtils;
	pp.YEAR_DAYS = 365.256898326;
	pp.PLANET_NAMES = ["mercury", "venus", "earth", "moon", "mars", "jupiter", "saturn", "uranus", "neptune", "pluto"];
	pp.normaliseExclude = ["a", "e", "x", "y", "d", "Dec", "RightAscension", "Declination", "altitude", "azimuth"];
	
	pp.cos = pp.NumberUtils.cosD;
	pp.sin = pp.NumberUtils.sinD;
	pp.atan2 = pp.NumberUtils.atan2D;
	pp.sqrt = Math.sqrt;
	//pp.normaliseDegrees = pp.normaliseDegrees;
	pp.simple = false;
	pp.lastDate = new Date();
	pp.lastSun;
	pp.lastLST;
	pp.longitude = -2.583;//15;//
	pp.latitude = 51.467;//60;//
	
	pp.normaliseDegrees = function(o) {	
		for (var i in o){
			if (pp.isNotNormaliseExcluded(i)) {
				o[i] %= 360;
				if(o[i] < 0){
					o[i] += 360;
				}
			}
		}
		return o;
	};
	
	pp.isNotNormaliseExcluded = function(i )  {
		for(var str in pp.normaliseExclude){
			if(pp.normaliseExclude[str] == i) return false;
		}
		return true;
	};
	pp.sun = function(date) {
		var d = pp.d(date)>>0;
		var N = 0.0;
		var i = 0.0;
		var w = pp.normaliseDegrees(282.9404 + 4.70935E-5 * d);
		var a = 1.000000;
		var e = 0.016709 - 1.151E-9 * d;
		var M = pp.NumberUtils.normaliseDegrees(356.0470 + 0.9856002585 * d);
		var ecl = 23.4393 - (3.563E-7 * d);
		var E = pp.E(M, e);
		var rv = pp.planetDistanceAndTrueAnomaly(E, e, a);
		var lonsun = rv.v + w;
		if(!pp.simple){
			var L = w + M;
			var xs = rv.r * pp.cos(lonsun);
			var ys = rv.r * pp.sin(lonsun);
			var xe = xs;
			var ye = ys * pp.cos(ecl);
			var ze = ys * pp.sin(ecl);
			var RA  = pp.NumberUtils.normaliseDegrees(pp.atan2( ye, xe ));
			var Dec = pp.atan2( ze, pp.sqrt(xe*xe+ye*ye) );
			var LST
			if(pp.lastDate.getTime() != date.getTime() || !pp.lastLST){
				pp.lastDate = date;
				pp.lastLST = pp.localSiderealTime(date, lonsun);
			}
			LST = pp.lastLST;
			
			var HA = pp.NumberUtils.normaliseDegrees(LST*15 - RA);
			
			var aax = pp.cos(HA) * pp.cos(Dec);
			var aay = pp.sin(HA) * pp.cos(Dec);
			var aaz = pp.sin(Dec);
			
			var xhor = aax * pp.sin(pp.latitude) - aaz * pp.cos(pp.latitude);
			var yhor = aay;
			var zhor = aax * pp.cos(pp.latitude) + aaz * pp.sin(pp.latitude);

			var azimuth  = pp.atan2( yhor, xhor ) + 180;
			var altitude = pp.atan2( zhor, pp.sqrt(xhor*xhor+yhor*yhor) );
			
			var o = {
				d : d,
				L : L,
				x : xe,
				y : ye,
				z : ze,
				r : rv.r,
				lonsun : lonsun,
				RA : RA,
				Dec : Dec,
				LST : LST,
				HA : HA,
				azimuth : azimuth,
				altitude : altitude,
				RightAscension : (pp.NumberUtils.degreesRightAscensionToHMS(pp.NumberUtils.normaliseDegrees(RA)).string),
				Declination : (pp.NumberUtils.degreesDeclinationToHMS(Dec).string)
			};
		} else {
			var o = {
					lonsun : lonsun
				};
		}
		return o;//pp.normaliseDegrees(o);
	};
	
	pp.planetDistanceAndTrueAnomaly = function(E , e , a )  {
		var xv = a * (pp.cos(E) - e);
		var yv = a * (pp.sqrt(1.0 - e * e) * pp.sin(E));
		var v = pp.atan2( yv, xv);
		var r = pp.sqrt(xv * xv + yv * yv);
		var o  = {};
		o.r = r;
		o.v = v;
		return o;
	};
	
	pp.planetHeliocentricPosition = function(N , w , i , rv)  {
		var xh = rv.r * ( pp.cos(N) * pp.cos(rv.v+w) - pp.sin(N) * pp.sin(rv.v+w) * pp.cos(i) );
	    var yh = rv.r * ( pp.sin(N) * pp.cos(rv.v+w) + pp.cos(N) * pp.sin(rv.v+w) * pp.cos(i) );
	    var zh = rv.r * ( pp.sin(rv.v+w) * pp.sin(i) );
		var lonecl  = pp.atan2( yh, xh );
		var latecl = pp.atan2( zh, pp.sqrt(xh*xh+yh*yh) );
		var o = {
			lonecl : lonecl,
			latecl : latecl
		};
		return pp.normaliseDegrees(o);
	};
	pp.plutoHeliocentricPosition = function(d){
		
	    var S  =   50.03  +  0.033459652 * d;
	    var P  =  238.95  +  0.003968789 * d;


	    var lonecl = 238.9508  +  0.00400703 * d
	            - 19.799 * pp.sin(P)     + 19.848 * pp.cos(P)
	             + 0.897 * pp.sin(2*P)    - 4.956 * pp.cos(2*P)
	             + 0.610 * pp.sin(3*P)    + 1.211 * pp.cos(3*P)
	             - 0.341 * pp.sin(4*P)    - 0.190 * pp.cos(4*P)
	             + 0.128 * pp.sin(5*P)    - 0.034 * pp.cos(5*P)
	             - 0.038 * pp.sin(6*P)    + 0.031 * pp.cos(6*P)
	             + 0.020 * pp.sin(S-P)    - 0.010 * pp.cos(S-P);

	    var latecl =  -3.9082
	             - 5.453 * pp.sin(P)     - 14.975 * pp.cos(P)
	             + 3.527 * pp.sin(2*P)    + 1.673 * pp.cos(2*P)
	             - 1.051 * pp.sin(3*P)    + 0.328 * pp.cos(3*P)
	             + 0.179 * pp.sin(4*P)    - 0.292 * pp.cos(4*P)
	             + 0.019 * pp.sin(5*P)    + 0.100 * pp.cos(5*P)
	             - 0.031 * pp.sin(6*P)    - 0.026 * pp.cos(6*P)
	                                   + 0.011 * pp.cos(S-P);

	   var r     =  40.72
	           + 6.68 * pp.sin(P)       + 6.90 * pp.cos(P)
	           - 1.18 * pp.sin(2*P)     - 0.03 * pp.cos(2*P)
	           + 0.15 * pp.sin(3*P)     - 0.14 * pp.cos(3*P);

		var o = {
				lonecl : lonecl,
				latecl : latecl,
				r : r
			};
			return pp.normaliseDegrees(o);
			
	}
	pp.getPosition = function(oe, special){
		var N = oe.N;
		var i = oe.i;
		var w = oe.w;
		var a = oe.a;
		var e = oe.e;
		var M = oe.M;
		var ecl = oe.ecl;
		var E  = pp.E(M, e, true);
		var rv
		var hp
		if(special == "pluto"){
			hp = pp.plutoHeliocentricPosition(oe.days);

			rv = {r:hp.r};pp.planetDistanceAndTrueAnomaly(E, e, a);
		} else {
			rv = pp.planetDistanceAndTrueAnomaly(E, e, a);
			hp = pp.planetHeliocentricPosition(N, w, i, rv);
		}
		if(!pp.simple){
			var xh = rv.r * pp.cos(hp.lonecl) * pp.cos(hp.latecl);
		    var yh = rv.r * pp.sin(hp.lonecl) * pp.cos(hp.latecl);
		    var zh = rv.r * pp.sin(hp.latecl);
			var sun
			if(pp.lastDate.getTime() != oe.date.getTime() || !pp.lastSun){
				pp.lastDate = oe.date;
				pp.lastSun = pp.sun(oe.date);
			}
			sun = pp.lastSun;
			var rs = sun.r;
			var lonsun = sun.lonsun;
			
			 var xs = rs * pp.cos(lonsun);
			 var ys = rs * pp.sin(lonsun);
			 var xg = xh + xs;
			 var yg = yh + ys;
			 var zg = zh;
			 if(special == "moon"){
				 xg = xh;
				 yg = yh;
				 zg = zh;
			 } else {

				 //console.log("is not moon")
				 xg = xh + xs;
				 yg = yh + ys;
				 zg = zh;
			 }
			 
			 
			 var xe = xg;
			 var ye = yg * pp.cos(ecl) - zg * pp.sin(ecl);
			 var ze = yg * pp.sin(ecl) + zg * pp.cos(ecl);
			 
			 var RA  = pp.NumberUtils.normaliseDegrees(pp.atan2( ye, xe ));
			 var Dec = pp.atan2( ze, pp.sqrt(xe*xe+ye*ye) );
			 var rg = pp.sqrt(xe*xe+ye*ye+ze*ze);
			 
			 var LST
			 if(pp.lastDate.getTime() != oe.date.getTime() || !pp.lastLST){
					pp.lastDate = oe.date;
					pp.lastLST = pp.localSiderealTime(oe.date, lonsun);
				}
			 LST = pp.lastLST;
			 var HA = pp.NumberUtils.normaliseDegrees(LST*15 - RA);
			 
			 var aax = pp.cos(HA) * pp.cos(Dec);
			 var aay = pp.sin(HA) * pp.cos(Dec);
			 var aaz = pp.sin(Dec);
			 
			 var xhor = aax * pp.sin(pp.latitude) - aaz * pp.cos(pp.latitude);
			 var yhor = aay;
			 var zhor = aax * pp.cos(pp.latitude) + aaz * pp.sin(pp.latitude);

			 var azimuth  = pp.atan2( yhor, xhor ) + 180;
			 var altitude = pp.atan2( zhor, pp.sqrt(xhor*xhor+yhor*yhor) );
			
			var o = {
				lonecl : hp.lonecl,
				latecl : hp.latecl,
				r : rg,
				RA : RA,
				Dec : Dec,
				LST : LST,
				HA : HA,
				azimuth : azimuth,
				altitude : altitude,
				RightAscension : (pp.NumberUtils.degreesRightAscensionToHMS(RA).string),
				Declination : (pp.NumberUtils.degreesDeclinationToHMS(Dec).string)
			};
		} else {
			var o = {
					lonecl : hp.lonecl
				};
		}
		return o;//pp.normaliseDegrees(o);
	};
	pp.localSiderealTime = function(date, lonsun){
		var GMST0 = pp.NumberUtils.normaliseDegrees(lonsun+180)/15;
		var UT = date.getHours()+((date.getMinutes()/60)+(date.getSeconds()/3600));
		return pp.NumberUtils.normaliseHours(GMST0 + UT + (pp.longitude/15));
	}
	pp.HA = function(lst){
		//return lst - 
	}
	pp.mercury = function(date){
		var d = pp.d(date);
		var oe = new OrbitalElements();
		oe.N = (48.3313 + 3.24587E-5 * d);
		oe.i = (7.0047 + 5.00E-8 * d);
		oe.w = pp.NumberUtils.normaliseDegrees(29.1241 + 1.01444E-5 * d);
		oe.a = (0.387098);
		oe.e = (0.205635 + 5.59E-10 * d);
		oe.M = pp.NumberUtils.normaliseDegrees(168.6562 + 4.0923344368 * d);
		oe.ecl = 23.4393 - (3.563E-7 * d);
		oe.days = pp.d(date);
		oe.date = date;
		return pp.getPosition(oe);
	};
	
	pp.venus = function(date){
		var d = pp.d(date);
		var oe = new OrbitalElements();
		oe.N = pp.NumberUtils.normaliseDegrees(76.6799 + 2.46590E-5 * d);
		oe.i = (3.3946 + 2.75E-8 * d);
		oe.w = pp.NumberUtils.normaliseDegrees(54.8910 + 1.38374E-5 * d);
		oe.a = (0.723330);
		oe.e = (0.006773 - 1.302E-9 * d);
		oe.M = pp.NumberUtils.normaliseDegrees(48.0052 + 1.6021302244 * d);
		oe.ecl = 23.4393 - (3.563E-7 * d);
		oe.days = pp.d(date);
		oe.date = date;
		return pp.getPosition(oe);
	};
	
	pp.earth = function(date) {
		var d = pp.d(date);
		var oe = new OrbitalElements();
		oe.N = (0.0);
		oe.i = (0.0);
		oe.w = pp.NumberUtils.normaliseDegrees(282.9404 + 4.70935E-5 * d);
		oe.a = (1.000000);
		oe.e = (0.016709 - 1.151E-9 * d);
		oe.M = pp.NumberUtils.normaliseDegrees(356.0470 + 0.9856002585 * d);
		oe.ecl = 23.4393 - (3.563E-7 * d);
		oe.days = pp.d(date);
		oe.date = date;
		var ro = pp.getPosition(oe);
		ro.lonecl+=180;
		return pp.normaliseDegrees(ro);
	};
	
	pp.moon = function(date){
		var d = pp.d(date);
		var oe = new OrbitalElements();
		oe.N = pp.NumberUtils.normaliseDegrees(125.1228 - 0.0529538083 * d);
		oe.i = (5.1454);
		oe.w = pp.NumberUtils.normaliseDegrees(318.0634 + 0.1643573223 * d);
		oe.a = (60.2666);
		oe.e = (0.054900);
		oe.M = pp.NumberUtils.normaliseDegrees(115.3654 + 13.0649929509 * d);
		oe.ecl = 23.4393 - (3.563E-7 * d);
		oe.days = pp.d(date);
		oe.date = date;
		return pp.getPosition(oe, "moon");
	};
	
	pp.mars = function(date){
		var d = pp.d(date);
		var oe = new OrbitalElements();
		oe.N = pp.NumberUtils.normaliseDegrees(49.5574 + 2.11081E-5 * d);
		oe.i = (1.8497 - 1.78E-8 * d);
		oe.w = pp.NumberUtils.normaliseDegrees(286.5016 + 2.92961E-5 * d);
		oe.a = (1.523688);
		oe.e = (0.093405 + 2.516E-9 * d);
		oe.M = pp.NumberUtils.normaliseDegrees(18.6021 + 0.5240207766 * d);
		oe.ecl = 23.4393 - (3.563E-7 * d);
		oe.days = pp.d(date);
		oe.date = date;
		return pp.getPosition(oe);
	};
	
	pp.jupiter = function(date){
		var d = pp.d(date);
		var oe = new OrbitalElements();
		oe.N = pp.NumberUtils.normaliseDegrees(100.4542 + 2.76854E-5 * d);
		oe.i = (1.3030 - 1.557E-7 * d);
		oe.w = pp.NumberUtils.normaliseDegrees(273.8777 + 1.64505E-5 * d);
		oe.a = (5.20256);
		oe.e = (0.048498 + 4.469E-9 * d);
		oe.M = pp.NumberUtils.normaliseDegrees(19.8950 + 0.0830853001 * d);
		oe.ecl = 23.4393 - (3.563E-7 * d);
		oe.days = pp.d(date);
		oe.date = date;
		return pp.getPosition(oe);
	};
	
	pp.saturn = function(date){
		var d = pp.d(date);
		var oe = new OrbitalElements();
		oe.N = pp.NumberUtils.normaliseDegrees(113.6634 + 2.38980E-5 * d);
		oe.i = (2.4886 - 1.081E-7 * d);
		oe.w = pp.NumberUtils.normaliseDegrees(339.3939 + 2.97661E-5 * d);
		oe.a = (9.55475);
		oe.e = (0.055546 - 9.499E-9 * d);
		oe.M = pp.NumberUtils.normaliseDegrees(316.9670 + 0.0334442282 * d);
		oe.ecl = 23.4393 - (3.563E-7 * d);
		oe.days = pp.d(date);
		oe.date = date;
		return pp.getPosition(oe);
	};
	
	pp.uranus = function(date){
		var d = pp.d(date);
		var oe = new OrbitalElements();
		oe.N = pp.NumberUtils.normaliseDegrees( 74.0005 + 1.3978E-5 * d);
		oe.i = (0.7733 + 1.9E-8 * d);
		oe.w = pp.NumberUtils.normaliseDegrees( 96.6612 + 3.0565E-5 * d);
		oe.a = pp.NumberUtils.normaliseDegrees(19.18171 - 1.55E-8 * d);
		oe.e = (0.047318 + 7.45E-9 * d);
		oe.M = pp.NumberUtils.normaliseDegrees(142.5905 + 0.011725806 * d);
		oe.ecl = 23.4393 - (3.563E-7 * d);
		oe.days = pp.d(date);
		oe.date = date;
		return pp.getPosition(oe);
	};
	
	pp.neptune = function(date){
		var d = pp.d(date);
		var oe = new OrbitalElements();
		oe.N = pp.NumberUtils.normaliseDegrees(131.7806 + 3.0173E-5 * d);
		oe.i = (1.7700 - 2.55E-7 * d);
		oe.w = pp.NumberUtils.normaliseDegrees(272.8461 - 6.027E-6 * d);
		oe.a = pp.NumberUtils.normaliseDegrees(30.05826 + 3.313E-8 * d);
		oe.e = (0.008606 + 2.15E-9 * d);
		oe.M = pp.NumberUtils.normaliseDegrees(260.2471 + 0.005995147 * d);
		oe.ecl = 23.4393 - (3.563E-7 * d);
		oe.days = pp.d(date);
		oe.date = date;
		return pp.getPosition(oe);
	};
	pp.pluto = function(date){
		var d = pp.d(date)
		var oe = new OrbitalElements();
		oe.ecl = 23.4393 - (3.563E-7 * d);
		oe.days = pp.d(date);
		oe.date = date;
		return pp.getPosition(oe, "pluto");
	}
	pp.d = function(date) {
		var Y = date.getFullYear();
		var M = date.getMonth()+1;
		var D = date.getDate();
		var d = 367 * Y - (7 * (Y + ((M + 9) / 12))) / 4 + (275 * M) / 9 + D - 730530;
		//var UT = (date.getUTCHours() + (date.getUTCMinutes() / 60)) / 24.0;
		//d += UT;
		var altD = ((date.getTime()-pp.epoch.getTime())/(1000*60*60*24));
		return altD;
	};
	
	pp.E = function(M, e, isPlanet) {
		var rE = M + e * (180 / Math.PI) * pp.sin(M) * (1 + e * pp.cos(M));
		var E1;
		var E0;
		if (isPlanet) {
			if (e > 0.06) {
				E0 = rE;
				E1 = E0 - (E0 - (180 / Math.PI) * e * pp.sin(E0) - M) / (1 - e * pp.cos(E0));
				while (E1 - E0 > 5E-3){
					E0 = E1;
					E1 = E0 - (E0 - (180 / Math.PI) * e * pp.sin(E0) - M) / (1 - e * pp.cos(E0));
				}
				rE = E1;
			}
		}
		return rE;
	}
	if(!window.$PlanetaryPositions){window.$PlanetaryPositions=pp};
}
function OrbitalElements() {
	var oe = this;
	oe.name;
	oe.N;
	oe.i;
	oe.w;
	oe.a;
	oe.e;
	oe.M;
	oe.ecl;
	oe.days;
	oe.date;
}


